/*******************************************************************************

SchedulerJS is an open source library designed to generate Google Calendar like
schedules. It is released under the MIT license which is included below.

MIT License

Copyright (c) 2016 Miguel A. Arvelo Morales

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File Revision History

Version=1.0 Date=Jul-23-2016 Author=Miguel A. Arvelo Morales
Initial Software Version

*******************************************************************************/

(function($){
    $.fn.ToSchedule = function( options ) {
      
      var getDatesInWeek = function(year, month, week) {
        var firstDateOfMonth = new Date(year, month, 1);
        var firstDayOfMonth = firstDateOfMonth.getDay();
        var firstDateOfWeek = new Date(firstDateOfMonth);
        
        firstDateOfWeek.setDate(
          firstDateOfWeek.getDate() + (firstDayOfMonth ? 7 - firstDayOfMonth : 0)
        );
        
        firstDateOfWeek.setDate(
          firstDateOfWeek.getDate() + (7 * (week - 1))
        );
        
        var datesOfMonthOnWeek = [];
        for (var i=0; i < 7; i++){
          datesOfMonthOnWeek.push(new Date(+firstDateOfWeek));
          firstDateOfWeek.setDate(firstDateOfWeek.getDate() + 1);
        }
        
        return datesOfMonthOnWeek;
      };
      
      var dateDiffDays = function(date1, date2) {
        return Math.round((date2-date1)/(1000*60*60*24));
      }
      
      var monthNames = ["January", "February", "March", "April",
                        "May", "June", "July", "August",
                        "September", "October", "November", "December"];
      var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday",
                      "Thursday", "Friday", "Saturday"];
      
      var todayObj = new Date();
      var settings = $.extend({
          data: null,
          month: todayObj.getMonth(),
          year: todayObj.getFullYear()
      }, options);
     
      var scheduleMonth = new Date( settings.year, settings.month, 1 );
      var lastDayInMonth = new Date( settings.year, settings.month + 1, 0);
      var daysInMonth = lastDayInMonth.getDate();
      var daysInWeek = 7;
      var scheduleMonthStartDay = scheduleMonth.getDay();
      
      var monthYear = monthNames[settings.month] + " " + settings.year;
      var monthYearDiv = document.createElement("div");
      monthYearDiv.innerHTML = monthYear;
      
      var gridRows = Math.ceil((scheduleMonthStartDay + daysInMonth) / daysInWeek);     
      
      var grid = document.createElement("div");
      grid.setAttribute("class", "ScheduleGrid");
      
      var currentDate = 1;
      
      // these will be used to generate the schedule structure
      var divRow, gridTable, activitiesTable, dateCell, activityCell, trGT, trAT, dateSquare;
      for (var w = -1; w < gridRows; w++) // Start week loop
      {
        divRow = document.createElement("div");
        divRow.setAttribute("class", "ScheduleRow");

        gridTable = document.createElement("table");
        gridTable.setAttribute("class", "ScheduleGridTable");
        trGT = gridTable.insertRow();
        
        // If it's the first row in the grid use it for 
        if (w < 0)
        {
          for (d = 0; d < 7; d++) // Day loop
          {
            dateCell = document.createElement("th");
            dateCell.innerHTML = dayNames[d];
            trGT.appendChild(dateCell);
          } // Day loop ends
        }
        else
        {
          divRow.setAttribute("id", "ScheduleRow_" + w);
          activityTable = document.createElement("table");
          activityTable.setAttribute("class", "ScheduleActivityTable");
          trAT = activityTable.insertRow();
          
          var weekDays = getDatesInWeek(settings.year, settings.month, w);
          
          console.log("Week " + (w+1) + " starts: " + weekDays[0].toDateString());
          console.log("Week " + (w+1) + " ends: " + weekDays[6].toDateString());
          
          for (d = 0; d < 7; d++) // Day loop
          {
            /* Grid definition */
            dateCell = trGT.insertCell();
            activityCell = trAT.insertCell();
            if ((w === 0 && d < scheduleMonthStartDay) || (currentDate > daysInMonth))
            {
              dateCell.setAttribute("class", "ScheduleInactiveCell");
            }
            else
            {
              dateSquare = document.createElement("div");
              dateSquare.setAttribute("class", "ScheduleDateSquare");
              if (scheduleMonth.getFullYear() === todayObj.getFullYear()
                  && scheduleMonth.getMonth() === todayObj.getMonth()
                  && currentDate === todayObj.getDate())
              {
                dateSquare.setAttribute("class", "ScheduleDateSquare ScheduleTodaySquare");
              }
              
              if((scheduleMonth.getFullYear() < todayObj.getFullYear())
                  || (scheduleMonth.getFullYear() === todayObj.getFullYear() 
                      && scheduleMonth.getMonth() < todayObj.getMonth())
                  || (scheduleMonth.getFullYear() === todayObj.getFullYear()
                      && scheduleMonth.getMonth() === todayObj.getMonth())
                      && currentDate < todayObj.getDate())
              {
                dateCell.setAttribute("class", "SchedulePastDateCell")
              }
              
              dateSquare.innerHTML = currentDate;
              currentDate = currentDate + 1;
              
              dateCell.appendChild(dateSquare);
            }
          } // Day loop ends
          
          // If activities were passed as part of the arguments
          if ( typeof settings.data !== "undefined" && settings.data !== null)
          { 
            var activities = settings.data.Activities; 
            
            
            for (var a in activities){
              trAT = activityTable.insertRow();
              
              var activityStartObj = activities[a].StartDate;
              var activityEndObj = activities[a].EndDate;

              console.log("  Activity: " + activities[a].Description)
              console.log("    Start Date: " + activityStartObj.toDateString())
              console.log("    End Date: " + activityEndObj.toDateString())
              
              var activityStartsAtOrBeforeWeek = activityStartObj <= weekDays[6];
              console.log("    " + activityStartObj + " <= " + weekDays[6] + " ? " + activityStartsAtOrBeforeWeek);
              
              var activityEndsAtOrAfterWeek = activityEndObj >= weekDays[0];
              console.log("    " + activityEndObj + " >= " + weekDays[0] + " ? " + activityEndsAtOrAfterWeek);
              
              var fallsWithinWeek = activityStartsAtOrBeforeWeek && activityEndsAtOrAfterWeek;
              console.log("    Falls in current week? " + (fallsWithinWeek ? "yes" : "no"));
              
              // if activity falls during this week
              if ( activityStartObj <= weekDays[6]
                  && activityEndObj >= weekDays[0]) {
               
                var colspan;
                var startingColumn;
                var millisecondsInADay = 1000 * 60 * 60 * 24;
                
                if(activityStartObj <= weekDays[0])
                {
                  startingColumn = 0;
                }
                else
                {
                  startingColumn = Math.round((activityStartObj - weekDays[0])/millisecondsInADay);
                }
                  
                if(activityEndObj >= weekDays[6])
                {
                  colspan = 7 - startingColumn;
                }
                else
                {
                  colspan = (7 - Math.round( (weekDays[6] - activityEndObj) /millisecondsInADay)) - startingColumn; 
                }
                
                for(var daysUntilStart = 0; daysUntilStart < startingColumn; daysUntilStart++)
                {
                  trAT.insertCell();
                }
                
                activityCell = trAT.insertCell();
                activityCell.setAttribute("colspan", colspan);
                activityCell.setAttribute("class", "ActivityCell")
                activityCell.innerHTML = activities[a].Description;
                
                var daysLeftInWeek = 7 - colspan - startingColumn;
                
                for(var daysAfterEnd = 0; daysAfterEnd < daysLeftInWeek; daysAfterEnd++)
                {
                  trAT.insertCell();
                }
              }
              
            }
            
            
          }
          divRow.appendChild(activityTable);
        }
        divRow.appendChild(gridTable);
        grid.appendChild(divRow);
      } // Week loop ends
     
      
      // In this context "this" refers to the object(s) for which this function
      // is being called. Note that we're doing our code inside an each call in case
      // we're calling it from a selector that returns multiple objects
      return this.each(function(){
          // In this context "this" refers to the current DOM object being dealt with
          
          // Clear the contents
          this.innerHTML = "";
          
          // Append the results
          this.appendChild(monthYearDiv);
          this.appendChild(grid); 
      });
    };
}(jQuery));
